from __future__ import annotations
from typing import List, Any
from io import IOBase 
from enum import Enum
import subprocess
from bcat.process.error import TimeoutExpired



#TODO: is polling for status  test ????
#TODO: clear stdout print


class Process(object):
    # Running -terminate|kill-> Terminating -wait-> Terminated
    # Running -wait-> Terminated
    class Status(Enum):
        Running = 1
        Terminating = 2
        Terminated = 3
        
    def __init__(self, impl: Any):
        self.__impl = impl
        self.__status = Process.Status.Running

    def status(self):
        return self.__status
    def impl(self) -> Any:
        return self.__impl

    def args(self)-> List[str]:
        return [ str(e) for e in self.__impl.args]
    def pid(self) -> int:
        return self.__impl.pid

    def stdin(self) -> Any:
        return self.__impl.stdin
    def stdout(self) -> Any:
        return self.__impl.stdout
    def stderr(self) -> Any:
        return self.__impl.stderr

    def return_code(self) -> int:
        return self.__impl.returncode

    def communicate(self, timeout: int | None) -> Any:
        try:
            return self.__impl.communicate(timeout)
        except subprocess.TimeoutExpired:
            raise TimeoutExpired(timeout, "process.communicate timeout")

    def _close_streams(self):
        if self.stdin() is not None and not self.stdin().closed:
            self.stdin().close()
        if self.stdout() is not None and not self.stdout().closed:
            self.stdout().close()
        if self.stderr() is not None and not self.stderr().closed:
            self.stderr().close()

    def terminate(self) -> Process:
        if self.__status == Process.Status.Running:
            print(f"{self} terminate")
            self._close_streams()
            self.impl().terminate()
            self.__status = Process.Status.Terminating
        return self

    def kill(self) -> Process:
        if self.__status == Process.Status.Running:
            print(f"{self} kill")
            self._close_streams()
            self.impl().kill()
            self.__status = Process.Status.Terminating
        return self


    def wait(self, timeout: int | None = None) -> Process:
        if self.__status in (Process.Status.Running, Process.Status.Terminating):
            print(f"{self} wait for -> {timeout} seconds")
            try:
                self.impl().wait(timeout)
            except subprocess.TimeoutExpired:
                raise TimeoutExpired(timeout, "process.wait timeout")
                
            self.__status = Process.Status.Terminated
        return self

    def is_terminated(self) -> bool:
        return self.impl().poll() is not None

    def __del__(self):
        self.kill().wait(4) #TODO: 4 ????

    def __repr__(self) -> str:
        return f"Process({self.pid()}, {self.args()}, {self.__status}, {self.stdin()},{self.stdout()},{self.stderr()})"
